package tt.dz.egobus.servicemanager.facade.interator;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.util.ReflectionUtils;
import tt.dz.egobus.base.context.SpringApplicationHolder;
import tt.dz.egobus.dubbo.context.EntryReference;
import tt.dz.egobus.dubbo.context.ReferenceConfigContext;
import tt.dz.egobus.dubbo.handler.ConsumerReferenceHandler;
import tt.dz.egobus.dubbo.utils.AopTargetUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author Anson
 * @date 17-3-20
 * @name
 * @since 1.0.1
 */
public class CglibProxyFacdeFactory implements FacadeConfigFactory {

    protected static Logger LOGGER = LoggerFactory.getLogger(CglibProxyFacdeFactory.class);


    private FacadeContext facadeContext;

    private Interceptor interceptor;

    private volatile boolean isDoScan = false;

    private Map<String, Object> context = new HashMap<String, Object>(10);

    @Override
    public void setContext(FacadeContext facadeContext) {
        this.facadeContext = facadeContext;
    }

    @Override
    public void setInterceptor(Interceptor interceptor) {
        this.interceptor = interceptor;
    }

    @Override
    public <E> E getBean(String facadeName, Class<E> clazz) {
        if (!isDoScan) {
            this.doScan();
        }
        Object obj = this.context.get(facadeName);
        if (obj == null || !clazz.isAssignableFrom(obj.getClass())) {
            return null;
        }
        return (E) obj;
    }

    public boolean isDoScan() {
        return isDoScan;
    }

    /**
     * 初始化当前扫描包类
     */
    public void doScan() {
        if (this.isDoScan) {
            return;
        }
        if (this.facadeContext == null) {
            return;
        }
        this.facadeContext.doScan();
        Iterator<Map.Entry<String, Class<?>>> iterator = this.facadeContext.iterator();
        if (iterator == null) {
            return;
        }
        Map.Entry<String, Class<?>> entry = null;
        if (SpringApplicationHolder.getApplicationContext() == null) {
            return;
        }
        ReferenceConfigContext cxt = SpringApplicationHolder.getBean(ReferenceConfigContext.class);
        if (cxt == null) {
            LOGGER.error("未找到在spring容器中的class={}，无法进行下一步处理", ReferenceConfigContext.class);
            return;
        }
        ConsumerReferenceHandler consumerReferenceHandler = null;

        CglibProxy proxy = new CglibProxy(this.interceptor);
        Class<?> curClass = null;
        String facadeName = null;
        Object obj = null;
        while (iterator.hasNext()) {
            entry = iterator.next();
            curClass = entry.getValue();
            facadeName = entry.getKey();
            if (!cxt.isReference(curClass)) {
                continue;
            }
            consumerReferenceHandler = new InnerConsumerReferenceHandler(curClass);
            consumerReferenceHandler.setReferenceContext(cxt);
            LOGGER.info("存在dubbo的相关API依赖,clazz={}", curClass);
            obj = proxy.getProxy(curClass);
            proxy.postProcessAfterInitialization(obj, facadeName, curClass, consumerReferenceHandler);
            this.context.putIfAbsent(entry.getKey(), obj);
        }
        this.isDoScan = true;
    }

    /**
     * @author Anson
     * @date 17-3-20
     * @name cglib代理
     * @since 1.0.1
     */
    private class CglibProxy implements MethodInterceptor {

        private final Interceptor interceptor;

        private CglibProxy(Interceptor interceptor) {
            this.interceptor = interceptor;
        }


        public Object getProxy(Class<?> clazz) {
            Enhancer enhancer = new Enhancer();
            //设置需要创建子类的类
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            //通过字节码技术动态创建子类实例
            return enhancer.create();
        }


        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if (!Modifier.isPublic(method.getModifiers())) {
                Object result = methodProxy.invokeSuper(obj, args);
                return result;
            }
            if (interceptor == null) {
                Object result = methodProxy.invokeSuper(obj, args);
                return result;
            }
            if (!interceptor.interceptBefore(obj, method, args)) {
                return null;
            }
            //通过代理类调用父类中的方法
            Object result = methodProxy.invokeSuper(obj, args);
            interceptor.interceptPost(obj, method, args, result);
            return result;
        }

        public Object postProcessAfterInitialization(Object bean, String facadeName, Class<?> curClass,
                                                     final ConsumerReferenceHandler consumerReferenceHandler)
                throws BeansException {
            Class<?> clazz = null;
            Object realBean = null;
            try {
                clazz = AopTargetUtils.getTargetClass(bean);
                realBean = AopTargetUtils.getTarget(bean);
            } catch (Exception e) {
                throw new RuntimeException("bean实例化获取目标对象异常", e);
            }
            LOGGER.info("facade  Bean实力化之后,存在dubbo的相关API依赖,facadeName={},class={}", facadeName, clazz);
            consumerReferenceHandler.referenceHandler(realBean);
            return bean;
        }


    }

    static class InnerConsumerReferenceHandler implements ConsumerReferenceHandler {

        protected static Logger LOGGER = LoggerFactory.getLogger(InnerConsumerReferenceHandler.class);

        private final Class<?> clazz;

        InnerConsumerReferenceHandler(Class<?> clazz) {
            this.clazz = clazz;
        }

        @Override
        public void referenceHandler(Object object) {
            if (object == null) {
                return;
            }
            this.validateContextNoNull();
            if (!this.context.isReference(clazz)) {
                LOGGER.error("不存存在dubbo的相关API依赖,class={}", clazz);
                return;
            }
            LOGGER.info("存在dubbo的相关API依赖,class={}", clazz);
            EntryReference[] entryReferences =
                    context.getClassReferenceFields(clazz);
            Object feildReference = null;
            Field field = null;
            if (entryReferences != null && entryReferences.length != 0) {
                for (EntryReference reference : entryReferences) {
                    feildReference = this.referenceField(object, reference);
                    if (feildReference == null) {
                        LOGGER.info("存在dubbo的相关API依赖,class={}", clazz);
                    }
                    field = reference.getField();
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, object, feildReference);
                    field.setAccessible(false);
                }
            }
        }


        private ReferenceConfigContext context;

        @Override
        public void setReferenceContext(ReferenceConfigContext context) {
            this.context = context;
        }


        @Override
        public Object referenceField(Object object, EntryReference reference) {
            if (object == null) {
                return object;
            }
            this.validateContextNoNull();
            //抛出UnknownReferenceConfigException运行异常
            reference.validate();
            return this.context.getReference(reference);
        }

        private void validateContextNoNull() {
            if (this.context == null) {
                throw new RuntimeException("当前的引用环境为空");
            }
        }
    }
}
